﻿using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Duellum.Core;
using Duellum.Map;
using Duellum.Wpf2d.Plugin;
using JpLabs.Geometry;
using Microsoft.Win32;
using Duellum.Wpf2d.Plugin.Drawing;

namespace Duellum.Wpf.Edit
{
	/// <summary>
	/// Interaction logic for EditMap.xaml
	/// </summary>
	public partial class EditMapWindow : Window
	{
		public enum EditMode
		{
			AddRemove, Inspect, Edit
		}

		private SaveFileDialog pSaveMapDialog;
		private MapCursorRenderer selectedCursor;
		private PointInt? clickStart;

		private readonly TrackableValue<EditMode> CurrentMode = new TrackableValue<EditMode>();
		
		public EditMapWindow()
		{
			InitializeComponent();
			
			mapViewer.DuelDomain = App.Current.CurrentDomain;
			
			//Create mouse cursor
			{
				var mouseCursor = new MapCursorDrawing(Colors.Red, 2);
				var mouseCursorRenderer = new MapMouseCursorRenderer(mouseCursor, MapZOrder.Medium);
				mapViewer.AddRenderer(mouseCursorRenderer);
			}
			
			//Create selected position cursor
			{
				this.selectedCursor = new MapCursorRenderer(
					new MapCursorDrawing(Colors.Blue, 3, 1),
					MapZOrder.Medium - 1
				);
				mapViewer.AddRenderer(selectedCursor);
				
				selectedCursor.PositionChanged	+= SelectedCursor_PositionChanged;
				mapViewer.LevelChanged			+= MapViewer_LevelChanged;
				mapViewer.VisualHost.MouseDown	+= MapVisualHost_MouseDown;
				mapViewer.VisualHost.MouseClick	+= MapVisualHost_MouseClick;
			}
			
			//btnViewOnly.Click	+= ((s,e) => CurrentMode.Value = EditMode.ViewOnly);
			btnInspect.Click	+= ((s,e) => CurrentMode.Value = EditMode.Inspect);
			btnAddRemove.Click	+= ((s,e) => CurrentMode.Value = EditMode.AddRemove);
			btnEdit.Click		+= ((s,e) => CurrentMode.Value = EditMode.Edit);

			CurrentMode.ValueChanged += Mode_ValueChanged;
			CurrentMode.Value = EditMode.Inspect;//default(EditMode);

			custCellEditor.DeleteEntries += CustCellEditor_DeleteEntries;
			custToolbox.LoadButtons();
		}

		private SaveFileDialog GetSaveMapDialog()
		{
			if (pSaveMapDialog == null) {
				pSaveMapDialog = new SaveFileDialog();
				pSaveMapDialog.InitialDirectory = Directory.GetCurrentDirectory();
				pSaveMapDialog.Title = "Save Map As";
				pSaveMapDialog.Filter = "Maps (*.mapx; *.map)|*.mapx;*.map|Any file (*.*)|*.*";
			}
			return pSaveMapDialog;
		}

		private void SaveCmdExecuted(object target, ExecutedRoutedEventArgs e)
		{
			/*
			var strB = new StringBuilder();
			DuelMapEncoder.SaveMapTo(mapViewer.Map, new StringWriter(strB));
			var xml = strB.ToString();
			/*/
			var dialog = GetSaveMapDialog();
			
			bool? ok = dialog.ShowDialog();
			if (ok.Value) {
				using (var writer = new StreamWriter(dialog.OpenFile())) {
					DuelMapEncoder.SaveMapTo(mapViewer.Map, writer);
				}
			}
			//*/
		}

		private void SaveCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		private void CustCellEditor_DeleteEntries(object sender, DeleteEntriesEventArgs e)
		{
			foreach (var entry in e.Entries) this.mapViewer.Map.RemoveEntry(entry);
			mapViewer.InvalidateMapVisual();
			Inspect();
		}
		
		private void Mode_ValueChanged(object sender, ValueChangedEventArgs<EditMapWindow.EditMode> e)
		{
			//btnViewOnly.IsChecked = CurrentMode == EditMode.ViewOnly;
			btnInspect.IsChecked = CurrentMode == EditMode.Inspect;
			btnAddRemove.IsChecked = CurrentMode == EditMode.AddRemove;
			btnEdit.IsChecked = CurrentMode == EditMode.Edit;
			
			switch (e.OldValue) {
				case EditMode.Inspect:	 ClearInspect(); break;
				case EditMode.AddRemove: custToolbox.Visibility = Visibility.Collapsed; break;
			}
			
			switch (e.NewValue) {
				case EditMode.Inspect:	 Inspect(); break;
				case EditMode.AddRemove: custToolbox.Visibility = Visibility.Visible; break;
			}
		}

		private void Window_Loaded(object sender, RoutedEventArgs e)
		{
			//execute mapviewer command bindings even when it's not focused
			this.CommandBindings.AddRange(mapViewer.CommandBindings);

			ApplicationCommands.Delete.InputGestures.Add(new KeyGesture(Key.Delete));
			this.CommandBindings.AddRange(custCellEditor.CommandBindings); //TODO: REVIEW
		}

		private void MapViewer_LevelChanged(object sender, RoutedPropertyChangedEventArgs<int> e)
		{
			//cursorHost.SelectedPosition = null;
			selectedCursor.Position = null;
		}

		private void MapVisualHost_MouseDown(object sender, MouseButtonEventArgs e)
		{
			clickStart = Wpf2dPlugin.PointToPosition(e.GetPosition((IInputElement)sender));
		}

		private void MapVisualHost_MouseClick(object sender, MouseButtonEventArgs e)
		{
			var position = Wpf2dPlugin.PointToPosition(e.GetPosition((IInputElement)sender));
			switch (e.ChangedButton) {
				case MouseButton.Left:	OnLeftClick(position, clickStart, Keyboard.Modifiers); break;
				case MouseButton.Right:	OnRightClick(position, clickStart, Keyboard.Modifiers); break;
			}
		}
		
		private void OnLeftClick(PointInt position, PointInt? clickStart, ModifierKeys modifierKeys)
		{
			switch (CurrentMode.Value) {
				case EditMode.Inspect: {
					selectedCursor.Position = position;
				} break;
				case EditMode.AddRemove: {
					//selectedCursor.Position = position;
					
					var type = custToolbox.Selected;
					
					if (type != null) {
						if (modifierKeys == ModifierKeys.None) {
							AddTypeToPosition(type, new PointInt(position.X, position.Y, mapViewer.Level));
						} else if ((modifierKeys & ModifierKeys.Shift) != 0) {
							for (int z = mapViewer.Map.Box.Lower.Z; z <= mapViewer.Map.Box.Upper.Z; z++) {
								AddTypeToPosition(type, new PointInt(position.X, position.Y, z));
							}
						}
					}
					
				} break;
			}
		}

		private void OnRightClick(PointInt position, PointInt? clickStart, ModifierKeys modifierKeys)
		{
			switch (CurrentMode.Value) {
				case EditMode.Inspect: {
					selectedCursor.Position = null;
				} break;
				case EditMode.AddRemove: {
					//selectedCursor.Position = position;
					
					if (modifierKeys == ModifierKeys.None) {
						ClearPosition(new PointInt(position.X, position.Y, mapViewer.Level));
					} else if ((modifierKeys & ModifierKeys.Shift) != 0) {
						for (int z = mapViewer.Map.Box.Lower.Z; z <= mapViewer.Map.Box.Upper.Z; z++) {
							ClearPosition(new PointInt(position.X, position.Y, z));
						}
					}
				} break;
			}
		}

		private void AddTypeToPosition(DuelType type, PointInt position)
		{
			mapViewer.Map.AddEntry(type, position);
			mapViewer.InvalidateMapVisual();
		}

		private void ClearPosition(PointInt position)
		{
			var list= mapViewer.Map
				.Query(position)
				.WhereIsNotGround()
				.ToArray();
			foreach (var e in list) mapViewer.Map.RemoveEntry(e);
			if (list.Length > 0) mapViewer.InvalidateMapVisual();
		}

		private void SelectedCursor_PositionChanged(object sender, ValueChangedEventArgs<PointInt?> e)
		{
			switch (CurrentMode.Value) {
				case EditMode.Inspect: Inspect(); break;
			}
		}

		private void ClearInspect()
		{
			custCellEditor.Clear();
			custCellEditor.Visibility = Visibility.Collapsed; //Content = null;
			//cellFrame.Children.Clear();
			//selectedEntry = null;
		}
		
		private void Inspect()
		{
			PointInt? pos = selectedCursor.Position;
			
			if (!pos.HasValue) {
				//custCellEditor.Visibility = Visibility.Collapsed;
				ClearInspect();
			} else {
				custCellEditor.Visibility = Visibility.Visible;
				var selectedPos = new PointInt(pos.Value.X, pos.Value.Y, mapViewer.Level);
				
				//var entries	= mapViewer.Map.Query(selectedPos).ToArray();
				
				//cellEditor.Content = new CellEditor(selectedPos, mapViewer);
				custCellEditor.Inspect(selectedPos, mapViewer.Map);
				//cellFrame.Children.Add(new CellEditor(selectedPos, mapViewer.Map));
				
				/*
				//cellFrame.Children.Add(new Frame(){ Content = new CellEditor()});
				
				var wpf2d = Wpf2dPlugin.Current;
				
				var entries	= mapViewer.Map.Query(selectedPos).ToArray();
				var objects = entries.Select(e => e.Object);
				var ground	= objects.Where(o => o.IsGround()).SingleOrDefault();
				var first	= objects.Where(o => !o.IsGround()).FirstOrDefault();
				
				{
					var text = string.Concat("Selected Position: ", selectedPos.ToString());
					cellFrame.Children.Add(new TextBlock(new Run(text)) { FontWeight = FontWeights.Bold });
				}
				
				{
					var imgGroup = new DrawingGroup();
					var imgSource = new DrawingImage(imgGroup);
					
					using (var dc = imgGroup.Append()) {
						const double zoom = 2;
						var hexSize = wpf2d.HexSize;
						var atOriginRect = new Rect(new Point(0, 0), hexSize);

						var borderImg
							= (ground != null)
							? wpf2d.GetHexBorderImg(zoom)
							: wpf2d.GetEmptyHexImg(mapViewer.Level, mapViewer.Map.GroundLevel, zoom);
						dc.DrawImage(borderImg, atOriginRect);
						
						if (ground != null) {
							var groundImg = wpf2d.GetSmallImage(ground);
							if (groundImg != null) dc.DrawImage(groundImg, atOriginRect);
						}
						
						if (first != null) {
							dc.DrawImage(
								wpf2d.GetSmallImage(first),
								atOriginRect
							);
						}
					}
					
					var img = new Image() { MaxWidth = 64, Source = imgSource };
					cellFrame.Children.Add(img);
				}

				if (entries.IsEmpty()) {
					var text = "Empty";
					cellFrame.Children.Add(new TextBlock(new Run(text)));
				} else {
					//var text = query
					//    .Select(o => o.Object.ToString())
					//    .Join(Environment.NewLine);
					//selectedPanel.Children.Add(
					//    new TextBlock(new Run(text)) {
					//        TextWrapping = TextWrapping.Wrap
					//    }
					//);
					
					{
						var text = string.Concat("Objects: ", objects.Count());
						cellFrame.Children.Add(new TextBlock(new Run(text)));
					}
					
					const string radioButtonGroup = "entries";
					//var radioButtonGroup = Guid.NewGuid().ToString();
					
					bool isFirst = true;

					foreach (var entry in entries) {
						//var type = (DuelType)entry.Object;
						//var text = type.Id.ToString();
						//var text = string.Concat("Obj id='", obj.Type.Id, "'");
						string text = (entry.Object is BaseDuelObj) ? string.Concat("Obj id='", entry.Object.Id, "'") : (string)entry.Object.Id;
						var radio = new RadioButton() {
							GroupName = radioButtonGroup,
							Tag = entry
						};
						radio.Checked += EntryRadio_Checked;
						radio.Content = new TextBlock(new Run(text));
						cellFrame.Children.Add(radio);
						if (isFirst) {
							isFirst = false;
							radio.IsChecked = true;
						}
					}
					{
						var deleteBtn = new Button() { Content = "Delete" };
						deleteBtn.Click += ((s,e) => {
							if (this.selectedEntry != null) {
								this.mapViewer.Map.RemoveEntry(selectedEntry);
								this.mapViewer.Redraw();
								this.Inspect();
							}
						});
						cellFrame.Children.Add(deleteBtn);
					}
				}
				//*/
			}
		}

		//void EntryRadio_Checked(object sender, RoutedEventArgs e)
		//{
		//    selectedEntry = (IDuelMapEntry)((FrameworkElement)sender).Tag;
		//}
	}
}
